package com.example.miaosha.service;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.example.miaosha.common.constants.Constants;
import com.example.miaosha.common.exception.BusinessException;
import com.example.miaosha.common.token.PtUserContextHolder;
import com.example.miaosha.common.utils.DateUtil;
import com.example.miaosha.common.utils.IdWorkerUtil;
import com.example.miaosha.config.mq.MqProperties;
import com.example.miaosha.dto.ChanelOrderDto;
import com.example.miaosha.entity.ActLogVo;
import com.example.miaosha.entity.SeActivity;
import com.example.miaosha.entity.SeOrder;
import com.example.miaosha.entity.base.BaseResponse;
import com.example.miaosha.entity.user.PtUser;
import com.example.miaosha.mapper.ActivityMapper;
import com.example.miaosha.mapper.SeOrderMapper;
import com.example.miaosha.response.base.BasePage;
import com.example.miaosha.vo.OrderVo;
import com.example.miaosha.vo.SeckillTestVo;
import org.apache.commons.lang3.StringUtils;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ExecutorService;

/**
 * 订单模块
 *
 * @Author
 * @Date 2022/1/14 13:37
 */
@Service
public class OrderService {

    @Resource
    private SeOrderMapper seOrderMapper;
    @Resource
    private ActivityMapper activityMapper;

    @Autowired
    private MqProperties mqProperties;
    @Autowired
    private RabbitTemplate rabbitTemplate;
    @Resource
    private ExecutorService executorService;

    /**
     * 秒杀活动生成订单
     *
     * @param buyNum
     * @param actId
     * @param userId
     */
    public void addOrder(Integer buyNum, String actId, String userId) {
        SeOrder seOrder = new SeOrder();
        seOrder.setId(IdWorkerUtil.nextId());
        seOrder.setOrdNo(IdWorkerUtil.nextId());
        seOrder.setPayNum(buyNum);
        seOrder.setActId(actId);
        seOrder.setUserId(userId);
        seOrder.setState(1);
        seOrder.setTime(DateUtil.getDate());
        seOrder.setPayMoney(activityMapper.selectById(actId).getActMoney());
        seOrderMapper.insert(seOrder);
    }

    /**
     * 付钱之后更新订单状态
     *
     * @param orderId
     */
    @Transactional(rollbackFor = Exception.class)
    public void payMoney(String orderId, String comId, String actId) {

        SeOrder seOrder = seOrderMapper.selectOne(new LambdaQueryWrapper<SeOrder>()
                .eq(SeOrder::getId, orderId)
                .eq(SeOrder::getUserId, PtUserContextHolder.get().getId())
                .eq(SeOrder::getActId, actId)
                .eq(SeOrder::getState, Integer.parseInt(Constants.NO))
        );
        if (Objects.isNull(seOrder)) {
            throw new BusinessException("查询到订单不存在");
        }
        seOrder.setState(Integer.parseInt(Constants.YES));
        SeActivity seActivity = activityMapper.selectOne(new LambdaQueryWrapper<SeActivity>().eq(SeActivity::getId, actId));
        if (Objects.isNull(seActivity)) {
            throw new BusinessException("查询到活动不存在");
        }
        if (StringUtils.isNotBlank(actId)) {
            // 说明是秒杀活动

        } else {
            // 正常支付

        }
        seOrderMapper.updateById(seOrder);
    }

    /**
     * 根据订单号查询订单
     *
     * @param orderId
     * @return
     */
    public SeOrder getOrder(String orderId) {
        SeOrder seOrder = seOrderMapper.selectOne(new LambdaQueryWrapper<SeOrder>().eq(SeOrder::getOrdNo, orderId));
        if (Objects.isNull(seOrder)) {
            throw new BusinessException("对应订单不存在");
        }
        return seOrder;
    }

    /**
     * 更新订单
     *
     * @param seOrder
     */
    public void updateOrder(SeOrder seOrder) {
        seOrderMapper.updateById(seOrder);
    }

    /**
     * 取消秒杀活动中的订单
     *
     * @param orderNo
     * @return
     */
    public Object cancelSeckillOrder(String orderNo) {
        SeOrder seOrder = seOrderMapper.selectOne(new LambdaQueryWrapper<SeOrder>().eq(SeOrder::getOrdNo, orderNo));
        if (Objects.isNull(seOrder)) {
            throw new BusinessException("待取消的订单不存在");
        }
        // 从这里开始就会有缓存一致性的问题，有不同的解决方案
        // 更新数据库-删除缓存
        // 任何删除缓存的操作时不可取的，那么也就意味着每次都需要去数据库查一下，再重新存入Redis中去
        // 这里也可以使用 消息队列订阅 binlog 日志，采用重试的方式保证数据一致性
        // 删除缓存，更新数据库
        // 延迟双删的延迟时间很难做到评估，1s的时间感觉还不如分布式锁

        // 我这里采用：更新数据库，更新缓存
        // 先更新数据库中的内容
        // 再异步发送消息抢占分布式锁去修改缓存中的库存，同时设置失败重试机制
        seOrderMapper.delete(new LambdaQueryWrapper<SeOrder>().eq(SeOrder::getId, seOrder.getId()));
        ChanelOrderDto dto = new ChanelOrderDto();
        dto.setActId(seOrder.getActId());
        dto.setBugNum(seOrder.getPayNum());
        // 异步操作
        executorService.submit(() -> {
            rabbitTemplate.convertAndSend(mqProperties.getDefaultExchange(),
                    mqProperties.getRouteKey(), dto);
        });
        return null;
    }

    public BaseResponse selectOrder(OrderVo vo) {
        PtUser ptUser = PtUserContextHolder.get();
        if (Objects.isNull(ptUser)) {
            return BaseResponse.error("用户未登录");
        }
        Page<SeOrder> page = new Page<SeOrder>(vo.getPageNum(), vo.getPageSize());
        IPage<SeOrder> iPage = seOrderMapper.selectPage(page, new LambdaQueryWrapper<SeOrder>()
                .eq(SeOrder::getUserId, ptUser.getId())
                .orderByDesc(SeOrder::getTime)
        );
        return BaseResponse.success(BasePage.getPage(iPage));
    }

    public BaseResponse selectByActId(OrderVo vo) {
        Page<SeOrder> page = new Page<SeOrder>(vo.getPageNum(), vo.getPageSize());
        IPage<SeOrder> iPage = seOrderMapper.selectPage(page, new LambdaQueryWrapper<SeOrder>()
                .eq(SeOrder::getActId, vo.getActId())
                .orderByDesc(SeOrder::getTime)
        );
        List<SeOrder> list = seOrderMapper.selectList(new LambdaQueryWrapper<SeOrder>()
                .eq(SeOrder::getActId, vo.getActId())
                .eq(SeOrder::getState, 0)
        );
        BigDecimal paymoney = list.stream().map(SeOrder::getPayMoney).reduce(BigDecimal.ZERO, BigDecimal::add);


        SeActivity activity = activityMapper.selectById(vo.getActId());
        BigDecimal bg = new BigDecimal(String.valueOf(activity.getActNum()));
        String allMoney = bg.multiply(activity.getActMoney()).toString();

        Integer userNum = seOrderMapper.selectCount(new LambdaQueryWrapper<SeOrder>()
                .eq(SeOrder::getActId, vo.getActId())
        );

        ActLogVo actLogVo = new ActLogVo();
        actLogVo.setPayMoney(paymoney.toString());
        actLogVo.setAllMoney(allMoney);
        actLogVo.setUserNum(String.valueOf(userNum));
        actLogVo.setActNum(String.valueOf(activity.getActNum()));

        BasePage.getPage(iPage).getData().add(actLogVo);

        return BaseResponse.success(BasePage.getPage(iPage));
    }


    public SeOrder selectBySeckillTestVo(SeckillTestVo vo) {
        return seOrderMapper.selectOne(new LambdaQueryWrapper<SeOrder>()
                .eq(SeOrder::getUserId, vo.getUserId())
                .eq(SeOrder::getActId,vo.getActId())
                .eq(SeOrder::getState,1)
        );
    }
}
